---------------------------------------------------------------------------------------------------------------------- --[[ scheme_type: generic author: Stohe modified_by: Alundaio --]] ---------------------------------------------------------------------------------------------------------------------- -------------------- local alife, level = alife, level -------------------- local ini = ini_file("ai_tweaks\\xr_help_wounded.ltx") local HelpInCombat = ini:r_bool_ex("settings","help_in_combat",true) local UseNewAnim = ini:r_bool_ex("settings","use_new_anim",true) local AnimCombat = ini:r_float_ex("settings","anim_combat_type") or 2 local AnimFree = ini:r_float_ex("settings","anim_free_type") or 1 local AlwaysHelpDistance = ini:r_float_ex("settings","always_help_distance") or 900 local HelpInCombatVisuals = utils_data.collect_sections(ini,{"help_in_combat_visuals"}) ini = nil function help_wounded(npc) if not (db.actor) then return end --state_mgr.set_state(npc,"guard") --printf("%s Helping wounded.",npc:name()) local id = npc and npc:id() local st = id and db.storage[id] and db.storage[id].help_wounded if not (st) then return end local selected_id = st.selected_id local selected_npc = selected_id and db.storage[selected_id] and db.storage[selected_id].object or selected_id and level.object_by_id(selected_id) -- reset scheme dependent variables if (st.selected_id and db.storage[st.selected_id]) then db.storage[st.selected_id].wounded_already_selected = nil end st.selected_id = nil st.vertex_id = nil st.vertex_position = nil st.nearest_dist = nil st.nearest_vertex = nil st.nearest_position = nil st.nearest_id = nil st.index = 1 if not (selected_npc) then return end xr_sound.set_sound_play(id, "wounded_medkit") alife_create_item("medkit_script", selected_npc) xr_wounded.unlock_medkit(selected_npc) game_statistics.increment_npc_statistic(npc,"wounded_helped") end ------------------------------------------ -- Localized Functions ------------------------------------------ local function has_enemy(npc) local be = npc:best_enemy() if (be and not IsWounded(be)) then return true end return false end local function combat_can_help(npc,vo,dist) local be = npc:best_enemy() if (be) then if (dist < 9) then return true end if (not be:see(npc)) then return true end return false end return true end ---------------------------------------------------------------------------------------------------------------------- -- EVALUATORS ---------------------------------------------------------------------------------------------------------------------- class "evaluator_wounded_exist" (property_evaluator) function evaluator_wounded_exist:__init(name, storage, npc) super (nil, name) self.a = storage end function evaluator_wounded_exist:find_valid_target() if (self.a.selected_id) then local vo = self.a.selected_id and self.a.selected_id ~= 65535 and self.a.selected_id ~= 0 and db.storage[self.a.selected_id] and db.storage[self.a.selected_id].object if (vo and IsStalker(vo) and vo:alive() and IsWounded(vo)) then return true end if (db.storage[self.a.selected_id]) then db.storage[self.a.selected_id].wounded_already_selected = nil end self.a.selected_id = nil end if not (db.OnlineStalkers) then return false end if not (self.a.index) then self.a.index = 1 end local npc = self.object local size = #db.OnlineStalkers if (size == 0 or self.a.index > size) then if (self.a.nearest_id and db.storage[self.a.nearest_id] and db.storage[self.a.nearest_id].wounded_already_selected == nil) then if (self.a.selected_id and db.storage[self.a.selected_id]) then db.storage[self.a.selected_id].wounded_already_selected = nil end self.a.selected_id = self.a.nearest_id self.a.vertex_id = self.a.nearest_vertex self.a.vertex_position = self.a.nearest_position self.a.index = 1 self.a.nearest_dist = nil self.a.nearest_vertex = nil self.a.nearest_position = nil self.a.nearest_id = nil db.storage[self.a.selected_id].wounded_already_selected = npc:id() return true end self.a.index = 1 self.a.nearest_dist = nil self.a.nearest_vertex = nil self.a.nearest_position = nil self.a.nearest_id = nil return false end local id = db.OnlineStalkers[self.a.index] self.a.index = self.a.index + 1 local vo = id and id ~= 0 and db.storage[id] and db.storage[id].object if not (vo) then return false end local se_obj = alife_object(id) if not (se_obj) then return false end if not (IsStalker(vo)) then return false end if not (vo:alive() and IsWounded(vo)) then return false end if (db.storage[id].wounded_already_selected) then return false end if (npc:relation(vo) >= game_object.enemy) then return false end if (db.storage[id].wounded == nil or db.storage[id].wounded.not_for_help == true) then return false end local vo_position = vec_set(vo:position()) local dist = npc:position():distance_to_sqr(vo_position) if (has_enemy(npc) and not combat_can_help(npc,vo,dist)) then return false end local see = dist <= 900 --or npc:see(vo) or npc:memory_time(vo) <= 30000 if (see) and (self.a.nearest_dist == nil or dist < self.a.nearest_dist) then local vertex = level.vertex_id(vo_position) if (vertex and vertex < 4294967295) then if level.vertex_position(vertex):distance_to_sqr(vo_position) > 16 then vertex = vo:level_vertex_id() end if (npc:accessible(vertex) and level.vertex_position(vertex):distance_to_sqr(vo_position) <= 15) then self.a.nearest_dist = dist self.a.nearest_vertex = vertex self.a.nearest_position = vo_position self.a.nearest_id = id end end end return false end function evaluator_wounded_exist:evaluate() --utils_data.debug_write("eva_wounded_exist") local npc = self.object if not (npc:alive()) then return false end if (self.a.help_wounded_enabled ~= true) then return false end if (IsWounded(npc)) then return false end if (xr_danger.has_danger(npc)) then return false end local vis = npc:get_visual_name() if not (HelpInCombat or HelpInCombatVisuals[vis]) then if (has_enemy(npc)) then return false end end if (self:find_valid_target()) then return true end return false end ---------------------------------------------------------------------------------------------------------------------- --Actions ---------------------------------------------------------------------------------------------------------------------- class "action_help_wounded" (action_base) function action_help_wounded:__init (npc_name,action_name, storage) super (nil, action_name) self.a = storage end function action_help_wounded:initialize() action_base.initialize(self) local npc = self.object npc:set_desired_position() npc:set_desired_direction() npc:set_path_type(game_object.level_path) --npc:set_dest_level_vertex_id(self.a.vertex_id) self.a.vertex_id = utils_obj.send_to_nearest_accessible_vertex(npc, self.a.vertex_id,"xr_help_wounded") if (has_enemy(npc)) then state_mgr.set_state(npc, "assault") else state_mgr.set_state(npc, "run") end db.storage[self.a.selected_id].wounded_already_selected = npc:id() end function action_help_wounded:execute() action_base.execute(self) local npc = self.object local st = self.a local state = state_mgr.get_state(npc) if (state == "help_victim_"..AnimCombat or state == "help_victim_"..AnimFree or state == "help_wounded") then return end if not (st.selected_id) then return end local dist = npc:position():distance_to_sqr(st.vertex_position) -- Stop helping victim if NPC is seen by best enemy local be = npc:best_enemy() if (be and not IsWounded(be) and dist > AlwaysHelpDistance) then if (be:see(npc)) then if (db.storage[st.selected_id]) then db.storage[st.selected_id].wounded_already_selected = nil end st.selected_id = nil return end end if (npc:level_vertex_id() == st.vertex_id) then if (UseNewAnim) then --if not (npc:object("medkit_script")) then --alife_create_item("medkit_script", npc) --end if (npc:active_item() and npc:best_weapon() and npc:weapon_unstrapped()) then state_mgr.set_state(npc, "help_victim_"..AnimCombat,nil,nil,nil,{fast_set = true}) else state_mgr.set_state(npc, "help_victim_"..AnimFree,nil,nil,nil,{fast_set = true}) end -- increase stalker rank for helping an ally npc:set_character_rank(npc:character_rank()+math.random(10,20)) else state_mgr.set_state(npc, "help_wounded",nil,nil,nil,{fast_set = true}) end else if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end st.vertex_id = utils_obj.send_to_nearest_accessible_vertex(npc, st.vertex_id,"xr_help_wounded:2") if (has_enemy(npc)) then state_mgr.set_state(npc, "assault") else state_mgr.set_state(npc, "run") end end end function action_help_wounded:finalize() --state_mgr.set_state(self.object, "idle") local st = self.a if (st.selected_id and db.storage[st.selected_id]) then db.storage[st.selected_id].wounded_already_selected = nil end st.selected_id = nil st.vertex_id = nil st.vertex_position = nil st.nearest_dist = nil st.nearest_vertex = nil st.nearest_position = nil st.nearest_id = nil st.index = 1 --[[ local medkit = self.object:object("medkit_script") local se_obj = medkit and alife_object(medkit:id()) if (se_obj) then alife_release(se_obj) end --]] action_base.finalize(self) end ---------------------------------------------------------------------------------------------------------------------- -- BINDER ---------------------------------------------------------------------------------------------------------------------- function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"help_wounded",section,temp) end function add_to_binder(npc,char_ini,scheme,section,st,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if (character_community(npc) == "zombied" or npc:section() == "actor_visual_stalker") then manager:add_evaluator(xr_evaluators_id.wounded_exist,property_evaluator_const(false)) temp.needs_configured = false return end local wp = world_property local evaluator = evaluator_wounded_exist("wounded_exist", st) temp.action = action_help_wounded (npc:name(),"action_help_wounded", st) if not evaluator or not temp.action then return end manager:add_evaluator(xr_evaluators_id.wounded_exist, evaluator) temp.action:add_precondition(wp(stalker_ids.property_alive, true)) temp.action:add_effect(wp(xr_evaluators_id.wounded_exist, false)) manager:add_action(xr_actions_id.wounded_exist, temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action then return end local wp = world_property temp.action:add_precondition(wp(xr_evaluators_id.sidor_wounded_base, false)) temp.action:add_precondition(wp(xr_evaluators_id.wounded_exist, true)) local action local vis = npc:get_visual_name() if (HelpInCombat or HelpInCombatVisuals[vis]) then --printf("ACTION_ALIFE_ID(xr_help_wounded.configure_actions): " .. tostring(stalker_ids.action_combat_planner)) action = manager:action(stalker_ids.action_combat_planner) if action then action:add_precondition( wp(xr_evaluators_id.wounded_exist,false) ) end --action = manager:action(xr_actions_id.state_mgr + 1) --if action then action:add_precondition( wp(xr_evaluators_id.wounded_exist,false) ) end end local p = { xr_actions_id.state_mgr + 2, xr_actions_id.alife } for i=1,#p do --printf("ACTION_ALIFE_ID(xr_help_wounded.configure_actions): " .. tostring(p[i])) action = manager:action(p[i]) if (action) then action:add_precondition( wp(xr_evaluators_id.wounded_exist,false) ) else printf("xr_help_wounded: no action id p[%s]",i) end end end function reset_generic_scheme(npc,scheme,section,stype,t) if not (t) then return end if not (t.help_wounded) then return end if (t.help_wounded.selected_id) then if (db.storage[t.help_wounded.selected_id]) then db.storage[t.help_wounded.selected_id].wounded_already_selected = nil end t.help_wounded.selected_id = nil end t.help_wounded.vertex_id = nil t.help_wounded.vertex_position = nil t.help_wounded.help_wounded_enabled = t.ini:r_string_ex(section,"help_wounded_enabled") or true end function is_under_help_wounded(npc) local mgr = npc:motivation_action_manager() if not mgr or not mgr:initialized() then return false end local current_action_id = mgr:current_action_id() return current_action_id == xr_actions_id.wounded_exist end